package com.jiegui.common.utils;

import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.DnsResolver;
import org.apache.http.conn.HttpConnectionFactory;
import org.apache.http.conn.ManagedHttpClientConnection;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.DefaultHttpResponseParserFactory;
import org.apache.http.impl.conn.ManagedHttpClientConnectionFactory;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.conn.SystemDefaultDnsResolver;
import org.apache.http.impl.io.DefaultHttpRequestWriterFactory;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;


public class HttpClientUtils {

    private static final String CHARSET = "UTF-8";

    private static final Logger log = LoggerFactory.getLogger(HttpClientUtils.class);



    static PoolingHttpClientConnectionManager manager = null;
    static CloseableHttpClient httpClient = null;

    public static synchronized CloseableHttpClient getHttpClient() {

        if (httpClient == null) {

            //注册访问协议相关的Socket工厂
            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.INSTANCE)
                    .register("https", SSLConnectionSocketFactory.getSystemSocketFactory())
                    .build();

            //HttpConnection 工厂:配置写请求/解析响应处理器
            HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connectionFactory
                    = new ManagedHttpClientConnectionFactory(DefaultHttpRequestWriterFactory.INSTANCE,
                    DefaultHttpResponseParserFactory.INSTANCE);

            //DNS 解析器
            DnsResolver dnsResolver = SystemDefaultDnsResolver.INSTANCE;

            //创建池化连接管理器
            manager = new PoolingHttpClientConnectionManager(socketFactoryRegistry, connectionFactory, dnsResolver);

            //默认为Socket配置
            SocketConfig defaultSocketConfig = SocketConfig.custom().setTcpNoDelay(true).build();
            manager.setDefaultSocketConfig(defaultSocketConfig);

            manager.setMaxTotal(300); //设置整个连接池的最大连接数
            //每个路由的默认最大连接，每个路由实际最大连接数由DefaultMaxPerRoute控制，而MaxTotal是整个池子的最大数
            //设置过小无法支持大并发(ConnectionPoolTimeoutException) Timeout waiting for connection from pool
            manager.setDefaultMaxPerRoute(200);//每个路由的最大连接数
            //在从连接池获取连接时，连接不活跃多长时间后需要进行一次验证，默认为2s
            manager.setValidateAfterInactivity(5 * 1000);

            //默认请求配置

            RequestConfig defaultRequestConfig = RequestConfig.custom()
                    .setConnectTimeout(2 * 1000) //设置连接超时时间，2s
                    .setSocketTimeout(20 * 1000) //设置等待数据超时时间，5s
                    .setConnectionRequestTimeout(2000) //设置从连接池获取连接的等待超时时间
                    .build();

            //创建HttpClient
            httpClient = HttpClients.custom()
                    .setConnectionManager(manager)
                    .setConnectionManagerShared(false) //连接池不是共享模式
                    .evictIdleConnections(60, TimeUnit.SECONDS) //定期回收空闲连接
                    .evictExpiredConnections()// 定期回收过期连接
                    .setConnectionTimeToLive(60, TimeUnit.SECONDS) //连接存活时间，如果不设置，则根据长连接信息决定
                    .setDefaultRequestConfig(defaultRequestConfig) //设置默认请求配置
                    .setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE) //连接重用策略，即是否能keepAlive
                    .setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE) //长连接配置，即获取长连接生产多长时间
                    .setRetryHandler(new DefaultHttpRequestRetryHandler(0, false)) //设置重试次数，默认是3次，当前是禁用掉（根据需要开启）
                    .build();


            //JVM 停止或重启时，关闭连接池释放掉连接(跟数据库连接池类似)

            Runtime.getRuntime().addShutdownHook(new Thread() {

                @Override
                public void run() {

                    try {
                        if (httpClient != null) {
                            httpClient.close();
                        }
                    } catch (IOException e) {
                        log.info("error when close httpClient:{}", e);

                    }

                }

            });

        }

        return httpClient;
    }

    public static String doPost(String url, Map<String, String> paramsMap) throws Exception {
        String result = null;
        //设置请求的状态参数
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(6000).setConnectTimeout(6000)
                .setSocketTimeout(6000).build();

        CloseableHttpClient httpClient = HttpClients.createDefault();
        try {
            HttpPost httpPost = new HttpPost(url);
            httpPost.setConfig(requestConfig);
            // 参数不为空
            if (null != paramsMap) {
                List<NameValuePair> params = new ArrayList<NameValuePair>();
                for (Map.Entry<String, String> entry : paramsMap.entrySet()) {
                    params.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
                }
                HttpEntity httpEntity = new UrlEncodedFormEntity(params, "UTF-8");
                httpPost.setEntity(httpEntity);
            } else {
                UrlEncodedFormEntity formEntity = null;
                httpPost.setEntity(formEntity);
            }

            CloseableHttpResponse response = httpClient.execute(httpPost);
            try {
                log.info("response.getStatusLine() ::{}",response.getStatusLine());
                HttpEntity entity = response.getEntity();
                result = EntityUtils.toString(entity);
            } finally {
                response.close();
            }
        } finally {
            httpClient.close();
        }
        return result;
    }

    public static String sendHttpMessagess(String url, String par) throws Exception {
        try {
            if (StringUtils.isEmpty(url) || null == par || par.isEmpty()) {
                return null;
            }
            List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
            if (url.indexOf("?") != -1) {    //有的通知地址带参数，需要处理  php公众号
                String csstr = url.substring(url.indexOf("?") + 1);
                log.info("csstr" + csstr);
                url = url.substring(0, url.indexOf("?"));
                String[] cs = csstr.split("&");
                for (String c : cs) {

                    params.add(new BasicNameValuePair(c.substring(0, c.indexOf("=")), c.substring(c.indexOf("=") + 1)));
                }

            }
            //创建POST请求
            HttpPost post = new HttpPost(url);
            post.setHeader("Content-type", "application/x-www-form-urlencoded; charset=" + CHARSET);
            post.setHeader("Accept", "application/json;charset=" + CHARSET);
            post.setHeader("Cache-Control", "no-cache");

            //请求参数
//			for(String key : map.keySet()){
//				params.add(new BasicNameValuePair(key,map.get(key)));
//			}

            StringEntity entityParams = new StringEntity(par, "utf-8");
            post.setEntity(entityParams);


            //发送请求
            // HttpClient client =getHttpClient();
            CloseableHttpClient client = HttpClients.createDefault();
            // HttpResponse response = client.execute(post);

            //   if(response.getStatusLine().getStatusCode() != HttpStatus.SC_OK){
            //    throw new ClientException("请求失败！");
            //   }
            CloseableHttpResponse response = null;

            response = client.execute(post);
            HttpEntity resEntity = response.getEntity();


            Map<String, String> resultMap = XmlUtils.toMap(EntityUtils.toByteArray(resEntity), "utf-8");
            Map<String, String> paramss = SignUtils.paraFilter(resultMap);
//            return null == resEntity ? "" : EntityUtils.toString(resEntity, CHARSET);
            return paramss.toString();
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(e.getMessage());
        }
    }



    /**
     * sendReq
     *
     * @param url
     * @param data
     * @param menthod
     * @return
     */
    public static String sendReq(String url, String data, String menthod) {

        HttpURLConnection urlConnection = null;
        BufferedOutputStream out;
        StringBuffer respContent = new StringBuffer();
        try {
            URL aURL = new URL(url);
            urlConnection = (HttpURLConnection) aURL.openConnection();

            urlConnection.setRequestMethod(menthod);

            urlConnection.setDoInput(true);
            urlConnection.setDoOutput(true);
            urlConnection.setUseCaches(false);

            urlConnection.setRequestProperty("Connection", "close");
            urlConnection.setRequestProperty("Content-Length", String.valueOf(data.getBytes("UTF-8").length));
            urlConnection.setRequestProperty("Content-Type", "text/html");

            urlConnection.setConnectTimeout(5000);
            urlConnection.setReadTimeout(5000);

            out = new BufferedOutputStream(urlConnection.getOutputStream());

            out.write(data.getBytes("UTF-8"));
            out.flush();
            out.close();
            int responseCode = urlConnection.getResponseCode();

            if (responseCode != 200) {
                throw new Exception("请求失败");
            }

            BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "utf-8"));
            String line;

            while ((line = reader.readLine()) != null) {
                respContent.append(line);
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        if (urlConnection != null) {
            urlConnection.disconnect();
        }
        return respContent.toString();
    }

    /**
     * sendReq
     *
     * @param url
     * @param menthod
     * @return
     */
    public static String sendReq(String url, String menthod) {
        HttpURLConnection urlConnection = null;
        BufferedOutputStream out;
        StringBuffer respContent = new StringBuffer();
        try {
            URL aURL = new URL(url);
            urlConnection = (HttpURLConnection) aURL.openConnection();

            urlConnection.setRequestMethod(menthod);

            urlConnection.setDoInput(true);
            urlConnection.setDoOutput(true);
            urlConnection.setUseCaches(false);

            urlConnection.setRequestProperty("Connection", "close");
            urlConnection.setRequestProperty("Content-Type", "text/html");

            urlConnection.setConnectTimeout(5000);
            urlConnection.setReadTimeout(5000);

            out = new BufferedOutputStream(urlConnection.getOutputStream());
            out.flush();
            out.close();
            int responseCode = urlConnection.getResponseCode();

            if (responseCode != 200) {
                throw new Exception("请求失败");
            }

            BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "utf-8"));
            String line;

            while ((line = reader.readLine()) != null) {
                respContent.append(line);
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        if (urlConnection != null) {
            urlConnection.disconnect();
        }
        return respContent.toString();
    }

    /**
     * @param url
     * @return
     */
    public static String sendReq(String url) {
        return sendReq(url, "GET");
    }

    /**
     * @param url
     * @return
     */
    public static JSONObject sendJsonReq(String url) {
        String json = sendReq(url);
        log.info(json);
        return JSONObject.parseObject(json);
    }

    /**
     * @param url
     * @param data
     * @param menthod
     * @return
     */
    public static JSONObject sendJsonReq(String url, String data, String menthod) {
        return JSONObject.parseObject(sendReq(url, data, menthod));
    }

    public static HttpServletRequest getHttpServletRequest() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    }

    public static Map<String, String> mapStringToMap(String str) {
        str = str.substring(1, str.length() - 1);
        String[] strs = str.split(",");
        Map<String, String> map = new HashMap<String, String>();
        for (String string : strs) {
            String key = string.split("=")[0];
            String value = string.split("=")[1];
            map.put(key, value);
        }
        return map;
    }

}

